package com.odaqiang.testsn.service.impl;
import com.odaqiang.testsn.domain.Sn;
import com.odaqiang.testsn.mapper.SnMapper;
import com.odaqiang.testsn.prefix.*;
import com.odaqiang.testsn.service.ISnService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.odaqiang.testsn.domain.Sn.Type;

@Service
public class SnServiceImpl implements ISnService, InitializingBean {

    @Autowired
    private SnMapper snMapper;
    private HiloOptimizer storageHiloOptimizer;
    private HiloOptimizer appHiloOptimizer;
    @Value("100") // 最大一次拿100个号到内存
    private int storageMaxLo;
    @Value("100")
    private int appMaxLo;

    /**
     * 生成序列号
     *
     * @param type 类型
     * @return 序列号
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public String generate(Type type) {
        if (type == null) {
            return null;
        }
        if (type == Type.STORAGE) {
            return storageHiloOptimizer.generate();
        } else if (type == Type.APP) {
            return appHiloOptimizer.generate();
        }
        return null;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        storageHiloOptimizer = new HiloOptimizer(Type.STORAGE, new StoragePrefix(), storageMaxLo);
        appHiloOptimizer = new HiloOptimizer(Type.APP, new AppPrefix(), appMaxLo);
    }

    private long getLastValue(Type type) {
        Sn sn = snMapper.selectbyType(type.getCode());
        long lastValue = sn.getLastValue();
        sn.setLastValue(lastValue + 2);
        int i = snMapper.updateByVersion(sn);
        if (i < 1) {
            throw new RuntimeException("更新sn错误");
        }
        return lastValue;
    }

    /**
     * 高低位算法
     */
    private class HiloOptimizer {

        private Type type;
        private BasePrefix prefix;
        private int maxLo;
        private int lo;
        private long hi;
        private long lastValue;

        public HiloOptimizer(Type type, BasePrefix prefix, int maxLo) {
            this.type = type;
            this.prefix = prefix;
            this.maxLo = maxLo;
            this.lo = maxLo + 1;
        }

        public synchronized String generate() {
            if (lo > maxLo) {
                lastValue = getLastValue(type);
                lo = lastValue == 0 ? 0 : 1;
                hi = lastValue * (maxLo + 1);
            }
            try {
                return prefix.buildPrefix(type, hi + lo++);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return String.valueOf(hi + lo++);
        }
    }
}
